<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<title>Assimp: Extending the Library</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr style="height: 56px;">
  <td style="padding-left: 0.5em;">
   <div id="projectname">Assimp
   &#160;<span id="projectnumber">v3.0 (July 2012)</span>
   </div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.1.1 -->
  <div id="navrow1" class="tabs">
    <ul class="tablist">
      <li><a href="index.html"><span>Main&#160;Page</span></a></li>
      <li class="current"><a href="pages.html"><span>Related&#160;Pages</span></a></li>
      <li><a href="namespaces.html"><span>Namespaces</span></a></li>
      <li><a href="annotated.html"><span>Classes</span></a></li>
      <li><a href="files.html"><span>Files</span></a></li>
    </ul>
  </div>
</div><!-- top -->
<div class="header">
  <div class="headertitle">
<div class="title">Extending the Library </div>  </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><h1><a class="anchor" id="General"></a>
General</h1>
<p>Or - how to write your own loaders. It's easy. You just need to implement the <a class="el" href="class_assimp_1_1_base_importer.html" title="FOR IMPORTER PLUGINS ONLY: The BaseImporter defines a common interface for all importer worker classe...">Assimp::BaseImporter</a> class, which defines a few abstract methods, register your loader, test it carefully and provide test models for it.</p>
<p>OK, that sounds too easy :-). The whole procedure for a new loader merely looks like this:</p>
<ul>
<li>
Create a header (<code><em>FormatName</em>Importer.h</code>) and a unit (<code><em>FormatName</em>Importer.cpp</code>) in the <code>&lt;root&gt;/code/</code> directory </li>
<li>
Add them to the following workspaces: vc8 and vc9 (the files are in the workspaces directory), CMAKE (code/CMakeLists.txt, create a new source group for your importer and put them also to ADD_LIBRARY( assimp SHARED)) </li>
<li>
Include <em>AssimpPCH.h</em> - this is the PCH file, and it includes already most Assimp-internal stuff.  </li>
<li>
<p class="startli">Open Importer.cpp and include your header just below the <em>(include_new_importers_here)</em> line, guarded by a #define </p>
<div class="fragment"><div class="line"><span class="preprocessor">#if (!defined assimp_BUILD_NO_FormatName_IMPORTER)</span></div>
<div class="line"><span class="preprocessor"></span>        ...</div>
<div class="line">#endif</div>
</div><!-- fragment --><p> Wrap the same guard around your .cpp!</p>
<p class="endli"></p>
</li>
<li>
Now advance to the <em>(register_new_importers_here)</em> line in the Importer.cpp and register your importer there - just like all the others do. </li>
<li>
Setup a suitable test environment (i.e. use AssimpView or your own application), make sure to enable the <a class="el" href="postprocess_8h.html#a64795260b95f5a4b3f3dc1be4f52e410ae420ce22fbbac9d0fd21fd92f2b630fa">aiProcess_ValidateDataStructure</a> flag and enable verbose logging. That is, simply call before you import anything: <div class="fragment"><div class="line">DefaultLogger::create(<span class="stringliteral">&quot;AssimpLog.txt&quot;</span>,Logger::VERBOSE)</div>
</div><!-- fragment -->  </li>
<li>
Implement the <a class="el" href="class_assimp_1_1_base_importer.html#a13588d3396ba5b7ed1f1cb46e0945cfd" title="Returns whether the class can handle the format of the given file.">Assimp::BaseImporter::CanRead()</a>, <a class="el" href="class_assimp_1_1_base_importer.html#ac67d9f5ceb26353d27d6be06cccad398" title="Imports the given file into the given scene structure.">Assimp::BaseImporter::InternReadFile()</a> and <a class="el" href="class_assimp_1_1_base_importer.html#a7a658f1192a37e336ba98fe701918b31" title="Called by #Importer::GetExtensionList for each loaded importer.">Assimp::BaseImporter::GetExtensionList()</a>. Just copy'n'paste the template from Appendix A and adapt it for your needs.  </li>
<li>
For error handling, throw a dynamic allocated ImportErrorException (see Appendix A) for critical errors, and log errors, warnings, infos and debuginfos with DefaultLogger::get()-&gt;[error, warn, debug, info].  </li>
<li>
Make sure that your loader compiles against all build configurations on all supported platforms. This includes <em>-noboost</em>! To avoid problems, see the boost section on this page for a list of all 'allowed' boost classes (again, this grew historically when we had to accept that boost is not THAT widely spread that one could rely on it being available everywhere).  </li>
<li>
Provide some <em>free</em> test models in <code>&lt;root&gt;/test/models/&lt;FormatName&gt;/</code> and credit their authors. Test files for a file format shouldn't be too large (<em>~500 KiB in total</em>), and not too repetive. Try to cover all format features with test data.  </li>
<li>
Done! Please, share your loader that everyone can profit from it!  </li>
</ul>
<h1><a class="anchor" id="properties"></a>
Properties</h1>
<p>You can use properties to chance the behavior of you importer. In order to do so, you have to overide BaseImporter::SetupProperties, and specify you custom properties in <a class="el" href="config_8h.html" title="Defines constants for configurable properties for the library.">config.h</a>. Just have a look to the other AI_CONFIG_IMPORT_* defines and you will understand, how it works.</p>
<p>The properties can be set with Importer::SetProperty***() and can be accessed in your SetupProperties function with Importer::GetProperty***(). You can store the properties as a member variable of your importer, they are thread safe.</p>
<h1><a class="anchor" id="tnote"></a>
Notes for text importers</h1>
<ul>
<li>
Try to make your parser as flexible as possible. Don't rely on particular layout, whitespace/tab style, except if the file format has a strict definition, in which case you should always warn about spec violations. But the general rule of thumb is <em>be strict in what you write and tolerant in what you accept</em>. </li>
<li>
Call <a class="el" href="class_assimp_1_1_base_importer.html#afc318aa0eb85b73f501156a50f3eb703" title="An utility for all text file loaders.">Assimp::BaseImporter::ConvertToUTF8()</a> before you parse anything to convert foreign encodings to UTF-8. That's not necessary for XML importers, which must use the provided IrrXML for reading.  </li>
</ul>
<h1><a class="anchor" id="bnote"></a>
Notes for binary importers</h1>
<ul>
<li>
Take care of endianess issues! <a class="el" href="namespace_assimp.html" title="Assimp&#39;s CPP-API and all internal APIs.">Assimp</a> importers mostly support big-endian platforms, which define the <code>AI_BUILD_BIG_ENDIAN</code> constant. See the next section for a list of utilities to simplify this task.  </li>
<li>
Don't trust the input data! Check all offsets!  </li>
</ul>
<h1><a class="anchor" id="util"></a>
Utilities</h1>
<p>Mixed stuff for internal use by loaders, mostly documented (most of them are already included by <em>AssimpPCH.h</em>): </p>
<ul>
<li>
<b>ByteSwap</b> (<em>ByteSwap.h</em>) - manual byte swapping stuff for binary loaders. </li>
<li>
<b>StreamReader</b> (<em>StreamReader.h</em>) - safe, endianess-correct, binary reading. </li>
<li>
<b>IrrXML</b> (<em>irrXMLWrapper.h</em>) - for XML-parsing (SAX. </li>
<li>
<b>CommentRemover</b> (<em>RemoveComments.h</em>) - remove single-line and multi-line comments from a text file. </li>
<li>
fast_atof, strtoul10, strtoul16, SkipSpaceAndLineEnd, SkipToNextToken .. large family of low-level parsing functions, mostly declared in <em>fast_atof.h</em>, <em>StringComparison.h</em> and <em>ParsingUtils.h</em> (a collection that grew historically, so don't expect perfect organization).  </li>
<li>
<b>ComputeNormalsWithSmoothingsGroups()</b> (<em>SmoothingGroups.h</em>) - Computes normal vectors from plain old smoothing groups.  </li>
<li>
<b>SkeletonMeshBuilder</b> (<em>SkeletonMeshBuilder.h</em>) - generate a dummy mesh from a given (animation) skeleton.  </li>
<li>
<b>StandardShapes</b> (<em>StandardShapes.h</em>) - generate meshes for standard solids, such as platonic primitives, cylinders or spheres.  </li>
<li>
<b>BatchLoader</b> (<em><a class="el" href="_base_importer_8h.html">BaseImporter.h</a></em>) - manage imports from external files. Useful for file formats which spread their data across multiple files.  </li>
<li>
<b>SceneCombiner</b> (<em>SceneCombiner.h</em>) - exhaustive toolset to merge multiple scenes. Useful for file formats which spread their data across multiple files.  </li>
</ul>
<h1><a class="anchor" id="mat"></a>
Filling materials</h1>
<p>The required definitions zo set/remove/query keys in <a class="el" href="structai_material.html" title="Data structure for a material.">aiMaterial</a> structures are declared in <em>MaterialSystem.h</em>, in a <a class="el" href="structai_material.html" title="Data structure for a material.">aiMaterial</a> derivate called <a class="el" href="structai_material.html" title="Data structure for a material.">aiMaterial</a>. The header is included by AssimpPCH.h, so you don't need to bother.</p>
<div class="fragment"><div class="line"><a class="code" href="structai_material.html" title="Data structure for a material.">aiMaterial</a>* mat = <span class="keyword">new</span> <a class="code" href="structai_material.html" title="Data structure for a material.">aiMaterial</a>();</div>
<div class="line"></div>
<div class="line"><span class="keyword">const</span> <span class="keywordtype">float</span> spec = 16.f;</div>
<div class="line">mat-&gt;<a class="code" href="structai_material.html#a0432dbdfd97ffe71838daf967a6b087e" title="Add a string property with a given key and type info to the material structure.">AddProperty</a>(&amp;spec, 1, <a class="code" href="material_8h.html#aeb1edba2c1e099a0a7055a82cabc9e87">AI_MATKEY_SHININESS</a>);</div>
<div class="line"></div>
<div class="line"><span class="comment">//set the name of the material:</span></div>
<div class="line">NewMaterial-&gt;AddProperty(&amp;<a class="code" href="structai_string.html" title="Represents an UTF-8 string, zero byte terminated.">aiString</a>(MaterialName.c_str()), <a class="code" href="material_8h.html#a204a0a39264125e160bd8fb2117f06e9">AI_MATKEY_NAME</a>);<span class="comment">//MaterialName is a std::string</span></div>
<div class="line"></div>
<div class="line"><span class="comment">//set the first diffuse texture</span></div>
<div class="line">NewMaterial-&gt;AddProperty(&amp;<a class="code" href="structai_string.html" title="Represents an UTF-8 string, zero byte terminated.">aiString</a>(Texturename.c_str()), <a class="code" href="material_8h.html#a18c395e1dd733f94d36353956a12776f">AI_MATKEY_TEXTURE</a>(<a class="code" href="material_8h.html#a7dd415ff703a2cc53d1c22ddbbd7dde0a3027af56603d5babd7e2efcf5ed1debd" title="The texture is combined with the result of the diffuse lighting equation.">aiTextureType_DIFFUSE</a>, 0));<span class="comment">//again, Texturename is a std::string</span></div>
</div><!-- fragment --><h1><a class="anchor" id="boost"></a>
Boost</h1>
<p>The boost whitelist: </p>
<ul>
<li>
<em>boost.scoped_ptr</em> </li>
<li>
<em>boost.scoped_array</em> </li>
<li>
<em>boost.format</em>  </li>
<li>
<em>boost.random</em>  </li>
<li>
<em>boost.common_factor</em>  </li>
<li>
<em>boost.foreach</em>  </li>
<li>
<em>boost.tuple</em> </li>
</ul>
<p>(if you happen to need something else, i.e. boost::thread, make this an optional feature. <code>assimp_BUILD_BOOST_WORKAROUND</code> is defined for <em>-noboost</em> builds)</p>
<h1><a class="anchor" id="appa"></a>
Appendix A - Template for BaseImporter's abstract methods</h1>
<div class="fragment"><div class="line"><span class="comment">// -------------------------------------------------------------------------------</span></div>
<div class="line"><span class="comment">// Returns whether the class can handle the format of the given file. </span></div>
<div class="line"><span class="keywordtype">bool</span> xxxxImporter::CanRead( <span class="keyword">const</span> std::string&amp; pFile, IOSystem* pIOHandler, </div>
<div class="line">        <span class="keywordtype">bool</span> checkSig)<span class="keyword"> const</span></div>
<div class="line"><span class="keyword"></span>{</div>
<div class="line">        <span class="keyword">const</span> std::string extension = GetExtension(pFile);</div>
<div class="line">        <span class="keywordflow">if</span>(extension == <span class="stringliteral">&quot;xxxx&quot;</span>) {</div>
<div class="line">                <span class="keywordflow">return</span> <span class="keyword">true</span>;</div>
<div class="line">        }</div>
<div class="line">        <span class="keywordflow">if</span> (!extension.length() || checkSig) {</div>
<div class="line">                <span class="comment">// no extension given, or we&#39;re called a second time because no </span></div>
<div class="line">                <span class="comment">// suitable loader was found yet. This means, we&#39;re trying to open </span></div>
<div class="line">                <span class="comment">// the file and look for and hints to identify the file format.</span></div>
<div class="line">                <span class="comment">// #Assimp::BaseImporter provides some utilities:</span></div>
<div class="line">                <span class="comment">//</span></div>
<div class="line">                <span class="comment">// #Assimp::BaseImporter::SearchFileHeaderForToken - for text files.</span></div>
<div class="line">                <span class="comment">// It reads the first lines of the file and does a substring check</span></div>
<div class="line">                <span class="comment">// against a given list of &#39;magic&#39; strings.</span></div>
<div class="line">                <span class="comment">//</span></div>
<div class="line">                <span class="comment">// #Assimp::BaseImporter::CheckMagicToken - for binary files. It goes</span></div>
<div class="line">                <span class="comment">// to a particular offset in the file and and compares the next words </span></div>
<div class="line">                <span class="comment">// against a given list of &#39;magic&#39; tokens.</span></div>
<div class="line"></div>
<div class="line">                <span class="comment">// These checks MUST be done (even if !checkSig) if the file extension </span></div>
<div class="line">                <span class="comment">// is not exclusive to your format. For example, .xml is very common </span></div>
<div class="line">                <span class="comment">// and (co)used by many formats.</span></div>
<div class="line">        }</div>
<div class="line">        <span class="keywordflow">return</span> <span class="keyword">false</span>;</div>
<div class="line">}</div>
<div class="line"></div>
<div class="line"><span class="comment">// -------------------------------------------------------------------------------</span></div>
<div class="line"><span class="comment">// Get list of file extensions handled by this loader</span></div>
<div class="line"><span class="keywordtype">void</span> xxxxImporter::GetExtensionList(std::set&lt;std::string&gt;&amp; extensions)</div>
<div class="line">{</div>
<div class="line">        extensions.insert(<span class="stringliteral">&quot;xxx&quot;</span>);</div>
<div class="line">}</div>
<div class="line"></div>
<div class="line"><span class="comment">// -------------------------------------------------------------------------------</span></div>
<div class="line"><span class="keywordtype">void</span> xxxxImporter::InternReadFile( <span class="keyword">const</span> std::string&amp; pFile, </div>
<div class="line">        <a class="code" href="structai_scene.html" title="The root structure of the imported data.">aiScene</a>* pScene, IOSystem* pIOHandler)</div>
<div class="line">{</div>
<div class="line">        boost::scoped_ptr&lt;IOStream&gt; file( pIOHandler-&gt;Open( pFile, <span class="stringliteral">&quot;rb&quot;</span>));</div>
<div class="line"></div>
<div class="line">        <span class="comment">// Check whether we can read from the file</span></div>
<div class="line">        <span class="keywordflow">if</span>( file.get() == NULL) {</div>
<div class="line">                <span class="keywordflow">throw</span> DeadlyImportError( <span class="stringliteral">&quot;Failed to open xxxx file &quot;</span> + pFile + <span class="stringliteral">&quot;.&quot;</span>);</div>
<div class="line">        }</div>
<div class="line">        </div>
<div class="line">        <span class="comment">// Your task: fill pScene</span></div>
<div class="line">        <span class="comment">// Throw a ImportErrorException with a meaningful (!) error message if </span></div>
<div class="line">        <span class="comment">// something goes wrong.</span></div>
<div class="line">}</div>
</div><!-- fragment --> </div></div><!-- contents -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Tue Jul 10 2012 17:55:53 for Assimp by &#160;<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/>
</a> 1.8.1.1
</small></address>
</body>
</html>
